package ru.niir.goodfil;

import org.apache.commons.cli.*;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.UnsupportedEncodingException;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;

public class Mailer {
    private String smtpHost;
    private String smtpUsername;
    private String smtpPassword;
    private String smtpPort = "25";

    private String dbHost;
    private String dbUsername;
    private String dbPassword;
    private String dbSchema;

    private boolean closeNotifications = true;
    private boolean sendTestLetter = false;
    private String testLetterMailbox = null;
    private String testLetterText = null;
    private String encodingFrom = null;
    private String encodingTo = null;
    private String encodingHeader = "utf8";
    private boolean appendUnicodeOptions = true;
    private boolean executeSetNames = true;
    private boolean executeSetCharacterSetResults = true;


    private List<Notification> notifications = new ArrayList<Notification>();

    public Mailer(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class.forName("com.mysql.jdbc.Driver").newInstance();

        Options options = new Options();
        options.addOption("Sh", "smtp-host", true, "SMTP hostname");
        options.addOption("Su", "smtp-username", true, "SMTP username");
        options.addOption("Sp", "smtp-password", true, "SMTP password");
        options.addOption("SP", "smtp-port", true, "SMTP port");

        options.addOption("Dh", "db-host", true, "Database host password");
        options.addOption("Du", "db-username", true, "Database username");
        options.addOption("Dp", "db-password", true, "Database password");
        options.addOption("Ds", "db-schema", true, "Database schema");

        //  ����� "�� ��������� ������������ �����������"
        options.addOption("dont_close", false, "Don't close notifications");

        //  ����� "��������� ������ �������� ������"
        options.addOption("test_letter", false, "Send only test letter");

        //  ����� "��������� ������ �������� ������": �������� ����
        options.addOption("test_letter_mailbox", true, "Send only test letter: mailbox");

        //  ����� "��������� ������ �������� ������": �����
        options.addOption("test_letter_text", true, "Send only test letter: text");

        //  ����� ������������� ����������� ��
        options.addOption("enc_from", true, "Source encoding for messages from DB");

        //  ����� ������������� ����������� �
        options.addOption("enc_to", true, "Target encoding for messages from DB");

        //  ����� �������� ��������� � ��������� ������
        options.addOption("enc_header", true, "Mail header encoding");

        //  ��� ����������� � �� ���������� unicode ���������
        options.addOption("db_utf", false, "Append unicode options to mysql connection string");

        //  ��������� set_names
        options.addOption("db_set_names", false, "Execute `set names utf8` statement after connection");

        //  ��������� set character_set_results=utf8
        options.addOption("db_set_character_set_results", false, "Execute `set character_set_results=utf8` statement after connection");

        CommandLineParser parser = new PosixParser();

        try {
            CommandLine cmd = parser.parse(options, args);
            smtpHost = cmd.getOptionValue("Sh");
            smtpUsername = cmd.getOptionValue("Su");
            smtpPassword = cmd.getOptionValue("Sp");
            dbHost = cmd.getOptionValue("Dh");
            dbUsername = cmd.getOptionValue("Du");
            dbPassword = cmd.getOptionValue("Dp");
            dbSchema = cmd.getOptionValue("Ds");

            if (cmd.hasOption("SP")) smtpPort = cmd.getOptionValue("SP");

            closeNotifications = !cmd.hasOption("dont_close");
            sendTestLetter = cmd.hasOption("test_letter");
            testLetterMailbox = cmd.getOptionValue("test_letter_mailbox");
            testLetterText = cmd.getOptionValue("test_letter_text");
            encodingFrom = cmd.getOptionValue("enc_from");
            encodingTo = cmd.getOptionValue("enc_to");
            encodingHeader = cmd.getOptionValue("enc_header");
            appendUnicodeOptions = cmd.hasOption("db_utf");
            executeSetNames = cmd.hasOption("db_set_names");
            executeSetCharacterSetResults = cmd.hasOption("db_set_character_set_results");
        } catch (ParseException e) {
            e.printStackTrace();
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp("mailer [OPTIONS]", options);
        }

        System.out.println("Mailer run at: " + getCurrentDateAsString());
        System.out.println("SMTP hostname  " + smtpHost);
        System.out.println("SMTP port      " + smtpPort);
        System.out.println("SMTP username  " + smtpUsername);
        System.out.println("SMTP password  " + smtpPassword);
        System.out.println("DB hostname    " + dbHost);
        System.out.println("DB username    " + dbUsername);
        System.out.println("DB password    " + dbPassword);
        System.out.println("DB schema      " + dbSchema);

        System.out.println("dont_close                   " + String.valueOf(!closeNotifications));
        System.out.println("test_letter                  " + String.valueOf(sendTestLetter));
        if (sendTestLetter) {
            System.out.println("test_letter_mailbox          " + String.valueOf(testLetterMailbox));
            System.out.println("test_letter_text             " + String.valueOf(testLetterText));
        }
        System.out.println("enc_from                     " + String.valueOf(encodingFrom));
        System.out.println("enc_to                       " + String.valueOf(encodingTo));
        System.out.println("enc_header                   " + String.valueOf(encodingHeader));
        System.out.println("db_utf                       " + String.valueOf(appendUnicodeOptions));
        System.out.println("db_set_names                 " + String.valueOf(executeSetNames));
        System.out.println("db_set_character_set_results " + String.valueOf(executeSetCharacterSetResults));
    }

    public static String getCurrentDateAsString() {
        return new SimpleDateFormat("dd.MM.yyyy hh:mm:ss").format(Calendar.getInstance().getTime());
    }

    public void run() throws SQLException {
        //  ������� ����������� ��� ��������
        if (sendTestLetter) {
            notifications.add(new Notification(testLetterMailbox, testLetterText));
        } else {
            takeFromDatabase();
        }

        //  �������� �����������
        send();

        //  �������� �����������
        if (closeNotifications) reportStateDatabase();
    }

    public static void main(String[] args) throws MessagingException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedEncodingException {
//        Properties props = new Properties();
//        props.setProperty("encodingFrom", "UTF-8");
//        props.setProperty("encodingTo", "UTF-8");
//        props.setProperty("encodingHeader", "utf8");
//        props.setProperty("message", "Hello world!");
//        props.setProperty("smtpHost", "smtp.yandex.ru");
//        props.setProperty("smtpUsername", "isystem.2010@yandex.ru");
//        props.setProperty("smtpPassword", "165004");
//        props.setProperty("smtpPort", "587");
//        props.setProperty("recipient", "sazonovkirill@gmail.com");
//        props.setProperty("subject", "Goodwill");
//        props.setProperty("from", "isystem.2010@yandex.ru");
//        postMail(props);
//
        try {
            new Mailer(args).run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void postMail(Properties props) throws MessagingException, UnsupportedEncodingException {
        String encodingFrom = props.getProperty("encodingFrom");
        String encodingTo = props.getProperty("encodingTo");
        String encodingHeader = props.getProperty("encodingHeader");
        String message = props.getProperty("message");
        String smtpHost = props.getProperty("smtpHost");
        String smtPort = props.getProperty("smtpPort");
        String smtpUsername = props.getProperty("smtpUsername");
        String smtpPassword = props.getProperty("smtpPassword");
        String from = props.getProperty("from");
        String recipient = props.getProperty("recipient");
        String subject = props.getProperty("subject");


        if (encodingFrom != null && encodingTo != null)
            message = new String(message.getBytes(encodingFrom), encodingTo);

        // EXPERIMENTAL
        if (encodingFrom != null && encodingTo != null)
            subject = new String(subject.getBytes(encodingFrom), encodingTo);

        boolean debug = false;

        //Set the host smtp address
        Properties p = new Properties();
        p.put("mail.smtp.host", smtpHost);
        p.put("mail.smtp.port", new Integer(smtPort));
      //  p.put("mail.smtp.auth", "true");

        // create some properties and get the default Session
        Session session = Session.getDefaultInstance(p, new SMTPAuthenticator(smtpUsername, smtpPassword));
        session.setDebug(debug);

        // create a message
        Message msg = new MimeMessage(session);

        // set the from and to address
        msg.setFrom(new InternetAddress(from));
        msg.setRecipients(Message.RecipientType.TO, new InternetAddress[]{new InternetAddress(recipient)});


        // Setting the Subject and Content Type

        msg.setSubject(subject);
        msg.setContent(message, "text/plain; charset=" + encodingHeader);
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
        }
        Transport.send(msg);

    }

    public void takeFromDatabase() throws SQLException {
        Connection conn = null;

        try {
            String cs = "jdbc:mysql://" + dbHost + "/" + dbSchema + "?user=" + dbUsername + "&password=" + dbPassword;
            if (appendUnicodeOptions) cs += "&encoding=UTF-8&useUnicode=true&characterEncoding=UTF-8";

            conn = DriverManager.getConnection(cs);

            Statement st = conn.createStatement();
            if (executeSetNames) st.execute("set names utf8");
            if (executeSetCharacterSetResults) st.execute("set character_set_results=utf8");

            PreparedStatement st2 = conn.prepareStatement("select * from `Notifications` where `IdNotificationState`=?");
            st2.setInt(1, NotificationStatus.NEW);

            notifications = new ArrayList<Notification>();
            ResultSet rs = st2.executeQuery();
            while (rs.next()) {
                int id = rs.getInt(1);
                String recipient = rs.getString(3);
                int status = rs.getInt(5);
                String message = rs.getString(4);
                String subject = rs.getString(7);
                notifications.add(new Notification(id, recipient, message, subject, status));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (Exception e) {
                }
            }
        }
    }

    public void send() {
        for (Notification notification : notifications) {
            try {
                Properties props = new Properties();
                props.setProperty("encodingFrom", encodingFrom);
                props.setProperty("encodingTo", encodingTo);
                props.setProperty("encodingHeader", encodingHeader);
                props.setProperty("message", notification.message);
                props.setProperty("smtpHost", smtpHost);
                props.setProperty("smtpPort", smtpPort);
                props.setProperty("smtpUsername", smtpUsername);
                props.setProperty("smtpPassword", smtpPassword);
                props.setProperty("recipient", notification.recipient);
                props.setProperty("subject", "Goodwill " + notification.subject);
                props.setProperty("from", smtpUsername);
                postMail(props);

                notification.status = NotificationStatus.SUCCESS;
            } catch (Exception me) {
                me.printStackTrace();
                notification.status = NotificationStatus.ERROR;
            }
        }
        System.out.println("Processed: " + notifications.size());
    }

    public void reportStateDatabase() throws SQLException {
        if (notifications.size() == 0) return;

        Connection conn = null;

        try {
            conn = DriverManager.getConnection("jdbc:mysql://" + dbHost + "/" + dbSchema + "?user=" + dbUsername + "&password=" + dbPassword);
            for (Notification notification : notifications) {
                PreparedStatement st = conn.prepareStatement("update `Notifications` set `IdNotificationState`=? where `Id`=?");
                st.setInt(1, notification.status);
                st.setInt(2, notification.id);
                st.executeUpdate();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (Exception e) {
                }
            }
        }
    }

    private static class SMTPAuthenticator extends javax.mail.Authenticator {
        private String smtpUsername;
        private String smtpPassword;

        public SMTPAuthenticator(String smtpUsername, String smtpPassword) {
            this.smtpUsername = smtpUsername;
            this.smtpPassword = smtpPassword;
        }

        public PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(smtpUsername, smtpPassword);
        }
    }

    private static class NotificationStatus {
        public static int NEW = 1;
        public static int PROCESSING = 2;
        public static int SUCCESS = 3;
        public static int ERROR = 4;
    }

    private static class Notification {
        private int id = 0;
        private String recipient;
        private String message;
        private String subject;
        private int status = NotificationStatus.NEW;

        private Notification(String recipient, String message) {
            this.recipient = recipient;
            this.message = message;
        }

        private Notification(int id, String recipient, String message, String subject, int status) {
            this.id = id;
            this.recipient = recipient;
            this.message = message;
            this.subject = subject;
            this.status = status;
        }
    }
}
